package biz

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/log"
	"go_private_im/internal/logic/conf"
	"strconv"
	"strings"
	"time"
)

type LogicHttpUsecase struct {
	conf *conf.Config
	repo LogicRepo
	log *log.Helper
}

type KeysArg struct {
	Op   int32    `form:"operation"`
	Keys []string `form:"keys"`
	Msg []byte
}

type UserArg struct {
	PublicArg
	AuthToken
	Op   int32    `form:"operation"`
	UserIds []int64 `form:"user_ids"`
	Msg []byte
}

type RoomArg struct {
	PublicArg
	AuthToken
	Op   int32  `form:"operation" binding:"required"`
	Room string `form:"room" binding:"required"`
	Msg []byte
}

type PushAllArg struct {
	Op    int32 `form:"operation" binding:"required"`
	Speed int32 `form:"speed"`
	Msg []byte
}

type AddRoomDetailsArg struct {
	AuthToken
	FormId int64 `form:"form_id" json:"form_id" binding:"required"`
	UserIds string `form:"user_ids" binding:"required"`
	Room string `form:"room" binding:"required"`
}


type DelRoomArg struct {
	Room string `form:"room" binding:"required"`
	UserId int64 `form:"user_id" binding:"required"`

}
type PublicArg struct {
	FormId int64 `form:"form_id" binding:"required" json:"form_id"`
	Type int `form:"type" binding:"required" json:"type"` // 1 文本消息 2 图片消息 3 语音 4 视频消息 9 通知消息
	Content string `form:"content" binding:"required" json:"content"` //消息 内容 通知消息
	NotifyLayout string `form:"notify_layout" json:"notify_layout"` //通知消息后缀 默认 跟在昵称后面 例如 欢迎 # 加入群聊
}

type AuthToken struct {
	Token string
}

//更新内容
func LayoutMsg(layout string,name string)  string {
	return strings.Replace(layout,"#",name,-1)
}


type RoomMsg struct {
	Id int64 `json:"id"`
	RoomId string `json:"room_id"`
	FormId int64 `json:"form_id"`
	Type int `form:"type" binding:"required" json:"type"` // 1 文本消息 2 图片消息 3 语音 4 视频消息 9 通知消息
	Content string `form:"content" binding:"required" json:"content"` //消息 内容 通知消息
	AddTime int64 `json:"add_time"`

}

func (r *RoomMsg) StructToByte() []byte {
	js,_ := json.Marshal(r)
	return js
}

type PrivateMsg struct {
	Id int64
	ToId int64
	FormId string
	Type int `form:"type" binding:"required" json:"type"` // 1 文本消息 2 图片消息 3 语音 4 视频消息 9 通知消息
	Content string `form:"content" binding:"required" json:"content"` //消息 内容 通知消息
	AddTime int64
}

type InitiativeRoomUserArg struct {
	AuthToken
	FormId int64 `form:"form_id" json:"form_id" binding:"required"`
	Room string `form:"room" binding:"required"`
}


func NewLogicHttpUsecase(c *conf.Config,logger log.Logger, l LogicRepo) *LogicHttpUsecase {
	return &LogicHttpUsecase{
		conf: c,
		repo: l,
		log: log.NewHelper(logger),
	}
}

func (l *LogicHttpUsecase) PushKeys(ctx context.Context,arg *KeysArg) (err error){

	servers, err := l.repo.ServersByKeys(ctx, arg.Keys)
	if err != nil {
		return
	}
	pushKeys := make(map[string][]string)
	for i, key := range arg.Keys {
		server := servers[i]
		if server != "" && key != "" {
			pushKeys[server] = append(pushKeys[server], key)
		}
	}
	for server := range pushKeys {
		if err = l.repo.PushMsg(ctx, arg.Op, server, pushKeys[server], arg.Msg); err != nil {
			return
		}
	}
	return nil
}

func (l *LogicHttpUsecase) PushUserIds (ctx context.Context, arg *UserArg) (err error) {
	_,err = l.Auth(ctx,arg.Token,arg.FormId)
	if err  != nil {
		return err
	}
	keyServers, _, err := l.repo.KeysByUserIds(ctx, arg.UserIds)
	if err != nil {
		return
	}
	keys := make(map[string][]string)
	for key, server := range keyServers {
		if key == "" || server == "" {
			l.log.Warnf("push key:%s server:%s is empty", key, server)
			continue
		}
		keys[server] = append(keys[server], key)
	}
	for server, keys := range keys {
		if err = l.repo.PushMsg(ctx, arg.Op, server, keys, arg.Msg); err != nil {
			return
		}
	}
	return
}



func (l *LogicHttpUsecase) PushRoom (ctx context.Context, arg *RoomArg) (err error)   {

	 user,err := l.Auth(ctx,arg.Token,arg.FormId)
	if err  != nil {
		return err
	}
	//检测是否是 群成员
	if !l.repo.UserExistRoomDetail(ctx,arg.Room,user.Id){
		return errors.NotFound("用户不在群中","用户不在群中")
	}
	roomMsg := &RoomMsg{
		FormId: arg.FormId,
		RoomId: arg.Room,
		Type: arg.Type,
		Content: arg.Content,
		AddTime: time.Now().Unix(),

	}
	//进行 消息持久化
	 if err = l.repo.CreateRoomMsg(roomMsg); err != nil{
	 	return
	 }
	 arg.Msg = roomMsg.StructToByte()
	return l.repo.BroadcastRoomMsg(ctx,arg.Op,arg.Room,arg.Msg)
}


func (l *LogicHttpUsecase) PushAll (ctx context.Context, arg *PushAllArg) (err error)   {
	return l.repo.BroadcastMsg(ctx,arg.Op,arg.Speed,arg.Msg)
}

func (l *LogicHttpUsecase) AddRoomUsers(ctx context.Context,arg *AddRoomDetailsArg) error  {
	// 验证 权限
	user,err := l.Auth(ctx,arg.Token,arg.FormId)
	if err  != nil {
		return err
	}
	// 验证 邀请人是否在群里
	if !l.repo.UserExistRoomDetail(ctx,arg.Room,user.Id) {
		return err
	}
	UserIds := strings.Split(arg.UserIds,",")
	//获取邀请人
	users,err := l.repo.GetUsers(ctx,UserIds)
	if err != nil {
		return err
	}
	//过滤邀请人 是否在群中出现

	Users := []*User{} //需要创建的
	userIds := []int64{}
	for _,v := range users {
		if !l.repo.UserExistRoomDetail(ctx,arg.Room,v.Id){
			Users  = append(Users,v)
			userIds = append(userIds,v.Id)
			}

	}

	if len(Users) < 1 {
		return errors.NotFound("用户已经存在","用户已经存在")
	}

	roomMsgs := []*RoomMsg{}
	roomDetails := []*RoomDetailInfo{}
	for _,user := range Users {
		roomMsg := &RoomMsg{
			FormId: user.Id,
			RoomId: arg.Room,
			Type: 9,
			AddTime: time.Now().Unix(),
		}
		roomMsg.Content = user.Name + "进入房间"
		roomMsgs = append(roomMsgs,roomMsg)
		roomDetail := &RoomDetailInfo{
			UserId: user.Id,
			AddTime:time.Now().Unix(),
			RoomId: arg.Room,
			Name: user.Name,
		}
		roomDetails = append(roomDetails,roomDetail)
	}
	if err := l.repo.AddRoomDetail(ctx,roomDetails); err != nil{
		return errors.NotFound("加入群聊失败","加入群聊失败")
	}
	if  err := l.repo.CreateRoomMsgs(roomMsgs); err != nil{
		return errors.NotFound("消息创建失败","消息创建失败")
	}
	// 通知被邀请人 加入 群聊
	keyServers, _, err := l.repo.KeysByUserIds(ctx, userIds)
	if err != nil {
		return nil
	}
	keys := make(map[string][]string)
	for key, server := range keyServers {
		if key == "" || server == "" {
			l.log.Warnf("push key:%s server:%s is empty", key, server)
			continue
		}
		keys[server] = append(keys[server], key)
	}

	for server,keys := range  keys {
		l.repo.NotifyUserAddRoom(ctx,server,keys,arg.Room)
	}
	//广播消息 谁进入群聊
	op,_ := strconv.Atoi(arg.Room)
	for _,v := range roomMsgs {
		Msg := v.StructToByte()
		l.repo.BroadcastRoomMsg(ctx,int32(op),arg.Room,Msg)
	}
	return nil
}


//主动加入群聊
func (l *LogicHttpUsecase)  InitiativeRoomUser(ctx context.Context,arg *InitiativeRoomUserArg) (err error) {
	user,err := l.Auth(ctx,arg.Token,arg.FormId)
	if err  != nil {
		return err
	}
	var  (
		roomInfo *RoomDetailInfo
	)
	//判断 群是否存在
	roomId,_ := strconv.Atoi(arg.Room)
	if roomInfo,err = l.repo.RoomExist(ctx,int64(roomId)); err != nil{
		return
	}
	if l.repo.UserExistRoomDetail(ctx,arg.Room,user.Id) {
		return errors.New(500,"请勿重新加群","请勿重新加群")
	}
	roomMsg := &RoomMsg{
		FormId: arg.FormId,
		RoomId: arg.Room,
		Type: 9,
		AddTime: time.Now().Unix(),
	}
	roomMsg.Content = user.Name + "进入房间"

	room := &RoomDetailInfo{
		UserId: user.Id,
		AddTime:time.Now().Unix(),
		RoomId: arg.Room,
		Name: user.Name,
	}

	//群存在 进行 加群处理
	fmt.Println(roomInfo,user)
	if err := l.repo.AddRoomDetail(ctx,[]*RoomDetailInfo{room}); err != nil{
		return errors.NotFound("加入群聊失败","加入群聊失败")
	}
	if  err := l.repo.CreateRoomMsgs([]*RoomMsg{roomMsg}); err != nil{
		return errors.NotFound("消息创建失败","消息创建失败")
	}

	// 通知被邀请人 加入 群聊
	keys,err := l.GetKeys(ctx,[]int64{user.Id})
	if err != nil {
		return
	}
	for server,keys := range  keys {
		l.repo.NotifyUserAddRoom(ctx,server,keys,arg.Room)
	}
	//广播消息 谁进入群聊
	err = l.repo.BroadcastRoomMsg(ctx,int32(roomId),arg.Room,roomMsg.StructToByte())
	return
}

func (l *LogicHttpUsecase) GetKeys(ctx context.Context,userids []int64) (keys map[string][]string,err error)  {
	// 通知被邀请人 加入 群聊
	keyServers, _, err := l.repo.KeysByUserIds(ctx, userids)
	keys = make(map[string][]string)
	fmt.Println(err,"Aaaaaaaaaaaaaaaaaaa")
	if err != nil {
		return
	}
	for key, server := range keyServers {
		if key == "" || server == "" {
			l.log.Warnf("push key:%s server:%s is empty", key, server)
			continue
		}
		keys[server] = append(keys[server], key)
	}
	return
}

type OutRoom struct {
	AuthToken
	FormId int64 `form:"form_id" json:"form_id" binding:"required"`
	Room string `form:"room" binding:"required"`
}
//主动 退出
func (l *LogicHttpUsecase) ActiveOutRoom(ctx context.Context ,arg *OutRoom) (err error) {
	user,err := l.Auth(ctx,arg.Token,arg.FormId)
	if err  != nil {
		return err
	}
	fmt.Println(user)
	var  (
		roomInfo *RoomDetailInfo
	)

	//判断 群是否存在
	roomId,_ := strconv.Atoi(arg.Room)
	if roomInfo,err = l.repo.RoomExist(ctx,int64(roomId)); err != nil{
		return
	}
	if !l.repo.UserExistRoomDetail(ctx,arg.Room,user.Id) {
		return errors.New(404,"您已经退出","您已经退出")
	}
	fmt.Println(roomInfo)
	//退出
	if err = l.repo.DelRoomDetail(ctx,arg.Room,user.Id); err != nil{
		return
	}
	roomMsg := &RoomMsg{
		FormId: arg.FormId,
		RoomId: arg.Room,
		Type: 9,
		AddTime: time.Now().Unix(),
	}
	roomMsg.Content = user.Name + "退出房间"

	if  err = l.repo.CreateRoomMsgs([]*RoomMsg{roomMsg}); err != nil{
		return errors.NotFound("消息创建失败","消息创建失败")
	}

	//通知 comet 将监听取消调
	keys,err := l.GetKeys(ctx,[]int64{user.Id})
	if err != nil {
		return
	}
	for server,keys := range  keys {
		err = l.repo.UserOutRoom(ctx,server,keys,arg.Room)
	}
	//广播消息 谁进入群聊
	err = l.repo.BroadcastRoomMsg(ctx,int32(roomId),arg.Room,roomMsg.StructToByte())
	return
}

type KickUserOutRoom struct {
	AuthToken
	FormId int64 `form:"form_id" json:"form_id" binding:"required"`
	UserIds string `form:"user_ids" binding:"required" json:"user_id"`
	Room string `form:"room" binding:"required"`
}

func (l *LogicHttpUsecase) KickUserOutRoom (ctx context.Context,arg *KickUserOutRoom)(err error)  {
	// 验证 权限
	user,err := l.Auth(ctx,arg.Token,arg.FormId)
	if err  != nil {
		return err
	}
	fmt.Println(user)
	userids := strings.Split(arg.UserIds,",")
	roomUserInfo,err := l.repo.GetRoomDetail(ctx,arg.Room,user.Id)
	//验证踢人者是否有权限

	if err != nil {
		return
	}
	if roomUserInfo.Type < 1 {
		return errors.InternalServer("权限不足","权限不足")
	}
	//检测 群成员

	roomUserInfos,err := l.repo.GetRoomDetails(ctx,arg.Room,userids)
	//
	fmt.Println(roomUserInfos)

	//通知 roomUserInfos 移除
	delUserId := []int64{}
	for _,v := range roomUserInfos {
		delUserId = append(delUserId,v.UserId)
	}
	//通知 comet 将监听取消调
	keys,err := l.GetKeys(ctx,delUserId)
	if err != nil {
		return
	}
	for server,keys := range  keys {
		err = l.repo.UserOutRoom(ctx,server,keys,arg.Room)
	}

	roomMsgs := []*RoomMsg{}
	//清理掉
	for _,v := range roomUserInfos {
		if err = l.repo.DelRoomDetail(ctx,arg.Room,v.UserId); err != nil{
			return
		}
		roomMsg := &RoomMsg{
			FormId: user.Id,
			RoomId: arg.Room,
			Type: 9,
			AddTime: time.Now().Unix(),
		}
		roomMsg.Content = user.Name + "退出房间"
		roomMsgs = append(roomMsgs,roomMsg)
	}
	if  err := l.repo.CreateRoomMsgs(roomMsgs); err != nil{
		return errors.NotFound("消息创建失败","消息创建失败")
	}
	roomId,_ := strconv.Atoi(arg.Room)
	for _,v := range roomMsgs {
		Msg := v.StructToByte()
		l.repo.BroadcastRoomMsg(ctx,int32(roomId),arg.Room,Msg)
	}

	return
}

type RoomlistArg struct {
	AuthToken
	UserId int64 `form:"user_id" binding:"required"`
}

type RoomlistRep struct {
	Id int64
	Name string
	NickName string
	Addtime string
}

//获取群列表
func (l *LogicHttpUsecase) GetRooms (ctx context.Context,arg *RoomlistArg) ([]*RoomlistRep,error) {
	_,err := l.Auth(ctx,arg.Token,arg.UserId)
	if err  != nil {
		return nil,err
	}
	roomList := []*RoomlistRep{}
	list,err  := l.repo.GetRoomDetailListByUserId(ctx,arg.UserId)
	for _,v := range list {
		data,err := l.repo.GetRoomsByRoomId(ctx,v.RoomId)
		if  err == nil{
			rep := &RoomlistRep{
				Id: data.Id,
				Name: data.Name,
				NickName: v.Name,
				Addtime: getTimeForm(v.AddTime),
			}

			roomList = append(roomList,rep)
		}
	}
	if err  != nil {
		return nil,err
	}
	return roomList,nil
}

type RoomMsgListArg struct {
	AuthToken
	UserId int64 `form:"user_id" binding:"required"`
	RoomId string `form:"room_id" binding:"required"`
}

type RoomMsglistRep struct {
	Id int64 `json:"id"`
	RoomId string `json:"room_id"`
	FormId int64 `json:"form_id"`
	Type int `json:"type"` // 1 文本消息 2 图片消息 3 语音 4 视频消息 9 通知消息
	Content string `json:"content"` //消息 内容 通知消息
	AddTime string `json:"add_time"`
}

func (l *LogicHttpUsecase) GetRoomMsg(ctx context.Context,arg *RoomMsgListArg) ([]*RoomMsglistRep,error) {
	_,err := l.Auth(ctx,arg.Token,arg.UserId)
	if err  != nil {
		return nil,err
	}
	list,err := l.repo.GetRoomMsgs(arg.RoomId)
	if err != nil {
		return nil,err
	}
	msgData := []*RoomMsglistRep{}
	for _,v := range list {
		data := &RoomMsglistRep{
			Id: v.Id,
			AddTime: getTimeForm(v.AddTime),
			FormId: v.FormId,
			RoomId: v.RoomId,
			Type: v.Type,
			Content: v.Content,
		}
		msgData = append(msgData,data)
	}
	return msgData,nil

}

func getTimeForm(strTime int64) string {
	//记12345,3那个位置的数这里我使用的15，也就是用24小时格式来显示，如果直接写03则是12小时am pm格式。
	timeLayout := "2006-01-02 15:04:05"
	datetime := time.Unix(strTime, 0).Format(timeLayout)
	return datetime
}

func (l *LogicHttpUsecase) Auth(ctx context.Context,token string,formId int64)  (*User,error) {
	user,err :=  l.repo.GetUser(ctx,formId)
	if err != nil {
		return nil, err
	}
	if user.Token != token {
		return nil, errors.NotFound("token 效验失败","token 效验失败")
	}
	return user,nil
}

type LoginArg struct {
	UserId int64 `json:"user_id"`
}

func (l *LogicHttpUsecase) Login(ctx context.Context,arg *LoginArg) (*AuthToken,error) {
	authToken,err := l.repo.Login(ctx,arg)
	if err != nil {
		return nil, err
	}
	return authToken,nil
}

func (l *LogicHttpUsecase) LoginOut(ctx context.Context,arg *LoginArg) error {
	if err := l.repo.LoginOut(ctx,arg); err != nil{
		return errors.NotFound("退出失败","退出失败")
	}
	return nil
}